package com.dage.salesflow.kit;

import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.MapUtils;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.*;

import java.util.*;

public class DbKit {

	/**
	 * 批量操作数据库时数量
	 */
	public static final int DB_BATCH_COUNT = 1024;

	/**
	 * 原有框架方法更新只会取modelList第一个元素的字段状态，批量更新的SQL全部相同，只是参数值不同
	 * 本方法会根据modelList中所有元素，生成不同的SQL和参数，分批分别执行
	 * 自动过滤所有null值属性
	 *
	 * @param modelList
	 * @param batchSize
	 * @return
	 * @see ：https://jfinal.com/share/2629
	 */
	public static List<Integer> batchListUpdate(List<? extends Model> modelList, int batchSize) {
		if (modelList == null || modelList.size() == 0)
			return ListUtils.newArrayList();
		Map<String, ModelBatchInfo> modelUpdateMap = MapUtils.newHashMap();

		for (Model model : modelList) {
			Set<String> modifyFlag = CPI.getModifyFlag(model);
			Config config = CPI.getConfig(model);
			Table table = TableMapping.me().getTable(model.getClass());
			String[] pKeys = table.getPrimaryKey();
			Map<String, Object> attrs = CPI.getAttrs(model);
			List<String> attrNames = new ArrayList<>();

			// the same as the iterator in Dialect.forModelSave() to ensure the order of the attrs
			for (Map.Entry<String, Object> e : attrs.entrySet()) {
				String attr = e.getKey();
				if (modifyFlag.contains(attr) && !config.getDialect().isPrimaryKey(attr, pKeys) && table.hasColumnLabel(attr))
					attrNames.add(attr);
			}
			for (String pKey : pKeys)
				attrNames.add(pKey);
			String columns = StrKit.join(attrNames.toArray(new String[attrNames.size()]), ",");
			ModelBatchInfo updateInfo = modelUpdateMap.get(columns);
			if (updateInfo == null) {
				updateInfo = new ModelBatchInfo();
				updateInfo.modelList = ListUtils.newArrayList();
				StringBuilder sql = new StringBuilder();
				config.getDialect().forModelUpdate(TableMapping.me().getTable(model.getClass()), attrs, modifyFlag, sql, new ArrayList<>());
				updateInfo.sql = sql.toString();
				modelUpdateMap.put(columns, updateInfo);
			}
			updateInfo.modelList.add(model);
		}
		return batchModelList(modelList, batchSize, modelUpdateMap);
	}

	private static List<Integer> batchModelList(List<? extends Model> modelList, int batchSize, Map<String, ModelBatchInfo> modelUpdateMap) {
		List<Integer> ret = ListUtils.newArrayListWithExpectedSize(modelList.size());
		//批量更新
		for (Map.Entry<String, ModelBatchInfo> entry : modelUpdateMap.entrySet()) {
			int[] batch = Db.batch(entry.getValue().sql, entry.getKey(), entry.getValue().modelList, batchSize);
			for (int i : batch) {
				ret.add(i);
			}
		}
		return ret;
	}

	/**
	 * 原有框架方法更新只会取modelList第一个元素的字段状态，批量插入的SQL全部相同，只是参数值不同
	 * 本方法会根据modelList中所有元素，生成不同的SQL和参数，分批分别执行
	 * 自动过滤所有null值属性
	 *
	 * @param modelList
	 * @param batchSize
	 * @return
	 * @see ：https://jfinal.com/share/2629
	 */
	public static List<Integer> batchListSave(List<? extends Model> modelList, int batchSize) {
		if (modelList == null || modelList.size() == 0)
			return ListUtils.newArrayList();
		Map<String, ModelBatchInfo> modelUpdateMap = MapUtils.newHashMap();

		for (Model model : modelList) {
			Config config = CPI.getConfig(model);
			Map<String, Object> attrs = CPI.getAttrs(model);
			int index = 0;
			StringBuilder columns = new StringBuilder();
			// the same as the iterator in Dialect.forModelSave() to ensure the order of the attrs
			for (Map.Entry<String, Object> e : attrs.entrySet()) {
				if (index++ > 0) {
					columns.append(',');
				}
				columns.append(e.getKey());
			}
			String cs = columns.toString();
			ModelBatchInfo batchInfo = modelUpdateMap.get(cs);
			if (batchInfo == null) {
				batchInfo = new ModelBatchInfo();
				batchInfo.modelList = ListUtils.newArrayList();
				StringBuilder sql = new StringBuilder();
				config.getDialect().forModelSave(TableMapping.me().getTable(model.getClass()), attrs, sql, new ArrayList());
				batchInfo.sql = sql.toString();
				modelUpdateMap.put(cs, batchInfo);
			}
			batchInfo.modelList.add(model);
		}
		return batchModelList(modelList, batchSize, modelUpdateMap);
	}

	/**
	 * 设置IN查询的sql和参数
	 *
	 * @param paras
	 * @param sb
	 * @param inParas
	 * @return
	 */
	public static StringBuilder buildInSqlPara(List<Object> paras, StringBuilder sb, Object[] inParas) {
		sb.append("(");
		for (int i = 0; i < inParas.length; i++) {
			paras.add(inParas[i]);
			if (i < inParas.length - 1) {
				sb.append("?,");
			} else {
				sb.append("?)");
			}
		}
		return sb;
	}

	public static class ModelBatchInfo {
		public String sql;
		public List modelList;
	}
}
